﻿namespace Smark.Core
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading;

    public class Log
    {
        private IList<Item> mItems = new List<Item>();
        private static IDictionary<Assembly, Log> mLogs = new Dictionary<Assembly, Log>();

        internal Log()
        {
            this.Writer = new LogWriteToConsole();
        }

        public void Add<T>(Exception e)
        {
            this.Add<T>(LogType.Error, e.Message, e.StackTrace);
        }

        public void Add<T>(LogType type, string message)
        {
            this.Add<T>(type, message, null);
        }

        public void Add<T>(LogType type, string message, string errorStackTrace)
        {
            if (((this.OutputType & type) > LogType.Off) || (this.OutputType == LogType.All))
            {
                Item state = new Item {
                    LogType = type,
                    Message = message,
                    ErrorStackTrace = errorStackTrace,
                    Type = typeof(T)
                };
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnWriteLog), state);
            }
        }

        public static Log Get<T>()
        {
            lock (mLogs)
            {
                Assembly key = typeof(T).Assembly;
                if (!mLogs.ContainsKey(key))
                {
                    Log log = new Log();
                    mLogs.Add(key, log);
                }
                return mLogs[key];
            }
        }

        private void OnWriteLog(object item)
        {
            Item item2 = (Item) item;
            this.Writer.Save(this, item2);
        }

        public static void SetWriteLogType<T>(LogType type)
        {
            Get<T>().OutputType = type;
        }

        public static void SetWriter<TYPE, WRITER>() where WRITER: ILogWriter, new()
        {
            Get<TYPE>().Writer = (default(WRITER) == null) ? ((ILogWriter) Activator.CreateInstance<WRITER>()) : ((ILogWriter) default(WRITER));
        }

        public static Log Write<T>(Exception e)
        {
            return Write<T>(LogType.Error, e);
        }

        public static Log Write<T>(LogType type, Exception e)
        {
            return Write<T>(type, e.Message, e.StackTrace);
        }

        public static Log Write<T>(LogType type, string message)
        {
            return Write<T>(type, message, null);
        }

        public static Log Write<T>(LogType type, string message, string errorStackTrace)
        {
            Log log = Get<T>();
            log.Add<T>(type, message, errorStackTrace);
            return log;
        }

        public static Log WriteFormat<T>(LogType type, string formatmsg, params object[] data)
        {
            return Write<T>(type, string.Format(formatmsg, data), null);
        }

        public IList<Item> Items
        {
            get
            {
                return this.mItems;
            }
        }

        internal string Name { get; set; }

        public LogType OutputType { get; internal set; }

        internal ILogWriter Writer { get; set; }

        public interface ILogWriter
        {
            void Save(Log log, Log.Item item);
        }

        [Serializable]
        public class Item
        {
            public Item()
            {
                this.Date = DateTime.Now;
            }

            public System.Reflection.Assembly Assembly
            {
                get
                {
                    return this.Type.Assembly;
                }
            }

            public DateTime Date { get; internal set; }

            public string ErrorStackTrace { get; set; }

            public Smark.Core.Log.LogType LogType { get; set; }

            public string Message { get; set; }

            public System.Type Type { get; set; }
        }

        public enum LogType
        {
            All = 1,
            Debug = 0x20,
            Debug1 = 0x40,
            Debug2 = 0x80,
            Error = 4,
            None = 2,
            Off = 0,
            Track = 0x100,
            Track1 = 0x200,
            Track2 = 0x400,
            Warning = 8
        }

        public class LogWriterToFile : Log.ILogWriter
        {
            private static IDictionary<Assembly, LogFile> mLogFiles = new Dictionary<Assembly, LogFile>();
            private string mPath;

            private LogFile GetLogFile(Log.Item item)
            {
                lock (mLogFiles)
                {
                    LogFile file;
                    string str = item.Assembly.GetName().Name + item.Date.ToString("yyyyMMdd") + ".log";
                    if (mLogFiles.ContainsKey(item.Assembly))
                    {
                        file = mLogFiles[item.Assembly];
                        if (file.Name != str)
                        {
                            file.Writer.Close();
                            file = new LogFile(this.Path, str);
                            mLogFiles[item.Assembly] = file;
                        }
                    }
                    else
                    {
                        file = new LogFile(this.Path, str);
                        mLogFiles.Add(item.Assembly, file);
                    }
                    return file;
                }
            }

            public void Save(Log log, Log.Item item)
            {
                LogFile logFile = this.GetLogFile(item);
                logFile.Writer.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", new object[] { item.Date, item.LogType, item.Type.FullName, item.Message, item.ErrorStackTrace, item.Assembly.GetName().Name });
                logFile.Writer.Flush();
            }

            protected string Path
            {
                get
                {
                    if (this.mPath == null)
                    {
                        this.mPath = Functions.AssemblyPath;
                    }
                    return this.mPath;
                }
            }

            private class LogFile
            {
                public LogFile(string path, string file)
                {
                    this.Name = file;
                    this.Writer = new StreamWriter(path + file, true, Encoding.UTF8);
                }

                public string Name { get; set; }

                public StreamWriter Writer { get; set; }
            }
        }

        public class LogWriteToConsole : Log.ILogWriter
        {
            public void Save(Log log, Log.Item item)
            {
                Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", new object[] { item.Date, item.LogType, item.Type.Name, item.Message, item.ErrorStackTrace, item.Assembly.GetName().Name });
            }
        }
    }
}

